package es.hekorusoft.udpcache.messages;

import java.io.ByteArrayOutputStream;
import java.util.concurrent.atomic.AtomicLong;

/*
 This file is part of UDPCache.

 UDPCache is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 UDPCache is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with UDPCache.  If not, see <http://www.gnu.org/licenses/>.
 */
public final class ClientMessage {

	public enum OpCode {
		GET, PUT, UPDATE, REMOVE, REMOVEALL, CREATE, STATS;
	}

	private static final AtomicLong seqGenerator = new AtomicLong(0);

	private String key;
	private String cache;
	private Object obj;
	private byte[] byteObj;
	private OpCode code;
	private long sequence;

	public ClientMessage() {

	}

	private ClientMessage(OpCode code, String cache, String key, Object obj) {
		this.code = code;
		this.key = key;
		this.cache = cache;
		this.obj = obj;
		this.sequence = seqGenerator.incrementAndGet();
		this.byteObj = null;

	}

	private ClientMessage(long seq, OpCode code, String cache, String key, Object obj, byte[] byteObj) {
		this.code = code;
		this.key = key;
		this.cache = cache;
		this.obj = obj;
		this.sequence = seq;
		this.byteObj = byteObj;
	}

	public final String getKey() {
		return key;
	}

	public final String getCache() {
		return cache;
	}

	public final Object getObj() {
		return obj;
	}

	public final OpCode getCode() {
		return code;
	}

	public final long getSequence() {
		return sequence;
	}

	public final byte[] getByteObj() {
		return byteObj;
	}

	public final static ClientMessage getMessage(String cache, String key) {
		return new ClientMessage(OpCode.GET, cache, key, null);
	}

	public final static ClientMessage putMessage(String cache, String key, Object obj) {
		return new ClientMessage(OpCode.PUT, cache, key, obj);
	}

	public final static ClientMessage updateMessage(String cache, String key) {
		return new ClientMessage(OpCode.UPDATE, cache, key, null);
	}

	public final static ClientMessage removeMessage(String cache, String key) {
		return new ClientMessage(OpCode.REMOVE, cache, key, null);
	}

	public final static ClientMessage removeAllMessage(String cache) {
		return new ClientMessage(OpCode.REMOVEALL, cache, null, null);
	}

	public final static ClientMessage createCacheMessage(String cache) {
		return new ClientMessage(OpCode.CREATE, cache, null, null);
	}

	public final static ClientMessage cacheStatsMessage(String cache) {
		return new ClientMessage(OpCode.STATS, cache, null, null);
	}

	public final static ClientMessage fromBytes(byte[] message) {
		long seq = 0;
		OpCode opCode = null;
		String cache = null;
		String key = null;
		Object obj = null;
		byte[] byteObj = null;

		StreamReader reader = new StreamReader(message);
		seq = reader.getLong();
		opCode = OpCode.values()[reader.getInt()];
		cache = reader.getString();
		if (opCode == OpCode.PUT || opCode == OpCode.GET || opCode == OpCode.REMOVE)
			key = reader.getString();
		if (opCode == OpCode.PUT) {
			byteObj = reader.getByteArray();
		}

		return new ClientMessage(seq, opCode, cache, key, obj, byteObj);
	}

	public final byte[] toByteArray() {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			StreamWriter.addLong(sequence, baos);
			StreamWriter.addInt(code.ordinal(), baos);
			if (cache != null)
				StreamWriter.addString(cache, baos);
			if (key != null)
				StreamWriter.addString(key, baos);
			if (obj != null)
				StreamWriter.addObject(obj, baos);
			return baos.toByteArray();
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
	}

	public final String toString() {
		return code.name() + " - " + sequence + " - " + cache + " - " + key + " - " + obj + " - " + byteObj;
	}

	// public void readExternal(ObjectInput in) throws IOException,
	// ClassNotFoundException {
	// sequence = in.readLong();
	// code = OpCode.values()[in.readInt()];
	// cache = in.readUTF();
	//
	// if (code == OpCode.PUT || code == OpCode.GET || code == OpCode.REMOVE)
	// key = in.readUTF();
	// if (code == OpCode.PUT) {
	// obj = in.readObject();
	// }
	//
	// }
	//
	// public void writeExternal(ObjectOutput out) throws IOException {
	// out.writeLong(sequence);
	// out.writeInt(code.ordinal());
	// if (cache != null) {
	// out.writeUTF(cache);
	// }
	// if (key != null) {
	// out.writeUTF(key);
	// }
	// if (obj != null) {
	// out.writeObject(obj);
	// }
	//
	// }
}
